package kuti.grp5;

import java.awt.geom.Point2D;
import java.math.BigInteger;
import java.util.List;
import kuti.*;

/**
 * This class is used to manipulate a small area of the lawn. This way the
 * calculation is faster.
 *
 * @author Marc Schorderet <marc.schorderet@edu.hefr.ch>
 * @author Vincenzo Do <vincenzo.do@edu.hefr.ch>
 * @version 1.0
 *
 * @see ILawnArea
 */
public class LawnArea implements ILawnArea {

    /*
     * The from point is the start point of the rectangular lawnArea
     */
    Point2D from;
    /*
     * The to point is the end point of the rectangular lawnArea
     */
    Point2D to;
    /*
     * The parameters of the simulation
     */
    ISimulParams simulParams;
    
    /*
     * The array with the height of all blades in the lawn
     */
    private int[][] lawn;
    /*
     * A list of all charging station, used to not grow the blades in these positions
     */
    private List<IChargingStation> chargingStations;
    
    /*
     * The calculated height of the rectangular lawnArea
     */
    public int height;
    /*
     * The calculated width of the rectangular lawnArea
     */
    public int width;
    
    /*
     * The coordinate X of the point from
     */
    public int fromX;
    /*
     * The coordinate Y of the from point
     */
    public int fromY;
    
    /*
     * The array that stores the height for the lawnArea
     */
    private int[][] currentLawn;

    /**
     * Constructor of the class, to create a new LawnArea we need a start and
     * end point, the original lawn and the simulationParameters. With the start
     * and end point we can define the height and width of a lawn area.
     *
     * @param from start point of the lawnArea
     * @param to end point of the lawnArea
     * @param lawn current lawn
     * @param simulParams parameters of the simulation
     */
    public LawnArea(Point2D from, Point2D to, int[][] lawn, ISimulParams simulParams) {

        if (from == null) {
            throw new IllegalArgumentException("The from point is not defined");
        }
        if (to == null) {
            throw new IllegalArgumentException("The to point is not defined");
        }
        if (Double.isNaN(from.getX()) || Double.isNaN(from.getY())) {
            throw new IllegalArgumentException("The from point is not a number");
        }
        if (Double.isNaN(to.getX()) || Double.isNaN(to.getY())) {
            throw new IllegalArgumentException("The to point is not a number");
        }
        if (lawn == null) {
            throw new IllegalArgumentException("The lawn array is not defined");
        }
        if (simulParams == null) {
            throw new IllegalArgumentException("The parameters are not defined");
        }

        this.from = from;
        this.to = to;
        this.lawn = lawn;
        this.simulParams = simulParams;

        height = (int) Math.ceil(to.getY() - from.getY());
        width = (int) Math.ceil(to.getX() - from.getX());
        fromX = (int) Math.ceil(from.getX());
        fromY = (int) Math.ceil(from.getY());
        currentLawn = new int[height + 1][width + 1];
        //Fill current
        fillCurrentLawn();

    }

    /**
     *
     * @return Returns the start point of the lawn area (lower - left)
     */
    @Override
    public Point2D getFrom() {
        return from;
    }

    /**
     *
     * @return Returns the end point of the lown area (upper - right)
     */
    @Override
    public Point2D getTo() {
        return to;
    }

    /**
     *
     * @return the currentLawn which contains the height of each blade in the
     * lawnArea
     */
    @Override
    public int[][] getBladeHeight() {
        return currentLawn;
    }

    /**
     * This method fills the new lawnArea with the values of the lawn.
     *
     * First index : rows [0...(toY-fromY)] Second index : colums
     * [0...(toX-fromX)]
     *
     */
    private void fillCurrentLawn() {
        for (int i = 0; i <= height; i++) {
            for (int j = 0; j <= width; j++) {
                if (i + fromY < ISimulParams.WORLD_HEIGHT && j + fromX < ISimulParams.WORLD_WIDTH) {
                    currentLawn[i][j] = lawn[i + fromY][j + fromX];
                }
            }
        }

    }

    /**
     * This method is used to grow the blades in the lawn, if a blade is under a
     * charging station his height will always be 0.
     *
     */
    public void growBladeInLawn() {

        for (int i = 0; i < lawn.length; i++) {
            for (int j = 0; j < lawn[i].length; j++) {
                if (simulParams.getBladeHMax() > lawn[i][j]) {
                    currentLawn[i][j] = lawn[i][j]++;
                }
            }
        }

        for (IChargingStation ch : chargingStations) {

            double bottomLeftX = ch.center().getX() - ch.width() / 2;
            double bottomLeftY = ch.center().getY() - ch.height() / 2;

            Point2D bottomLeft = RadarHelper.doubleToIntPoint(new Point2D.Double(bottomLeftX, bottomLeftY), false, false);

            double topRightX = ch.center().getX() + ch.width() / 2;
            double topRightY = ch.center().getY() + ch.height() / 2;

            Point2D topRight = RadarHelper.doubleToIntPoint(new Point2D.Double(topRightX, topRightY), true, true);

            for (int i = (int) bottomLeft.getX(); i <= (int) topRight.getX(); i++) {
                for (int j = (int) bottomLeft.getY(); j <= (int) topRight.getY(); j++) {
                    lawn[j][i] = 0;
                }
            }
        }

    }

    /**
     * This method is used to cut a blade in the position x and y.
     *
     * @param y coordinate in y
     * @param x coordinate in x
     */
    public void cutBladeInArea(int y, int x) {
        lawn[y][x] = 0;
    }

    /**
     * This method is used to set the list of all charging station in the
     * simulation.
     *
     * @param chargingStations list of all charging stations.
     */
    public void setChargingStationList(List<IChargingStation> chargingStations) {
        this.chargingStations = chargingStations;
    }
}
